﻿Imports System.IO
Imports System.Text.RegularExpressions

Public Class MemoryInfoParser


    Private Shared hexPattern As New Regex("[0-9a-fA-F]{8}")
    Private Shared promptPattern As New Regex("[0-9]:[0-9]{1,3}>")
    Private Shared headerPattern As New Regex("Heap\s*?Gen0\s*?Gen1\s*?Gen2\s*?LOH")
    Private Shared freeSpacePattern As New Regex("Free space:")
    Private Shared rowPattern As New Regex("Heap([0-9])\s*?([0-9]{1,12})\s*?([0-9]{1,12})\s*?([0-9]{1,12})\s*?([0-9]{1,12})")
    Private Shared percentagePattern As New Regex("SOH: ([0-9]{1,3})% LOH: ([0-9]{1,3})%")
    Private Shared eeheapPattern As New Regex("Number of GC Heaps: [0-9].*?GC Heap Size.*?bytes.", RegexOptions.Singleline)

    Public Shared Function Parse(ByVal text As String) As MemoryInfo
        'look for results table header(s)
        'multiple (take first fow now, prompt later?)
        Dim info As New MemoryInfo

        Dim startIndex As Integer
        Dim headerMatches As MatchCollection = headerPattern.Matches(text)
        If headerMatches.Count = 0 Then
            info.DbgOutput = "No heapstat found"
            Return info

        ElseIf headerMatches.Count > 1 Then
            MessageBox.Show("Multiple results found in text. Taking first one")
            startIndex = headerMatches.Item(0).Index + headerMatches.Item(0).Length
        Else
            'one header found
            ' process all lines after as results
            startIndex = headerMatches.Item(0).Index + headerMatches.Item(0).Length
        End If

        'get substring of text  from start
        Dim section As String = text.Substring(startIndex)

        Dim reader As New StringReader(section)
        Dim emptyLineCount As Integer = 0
        Dim noMatchCount As Integer = 0
        Dim isFreeSpaceSection As Boolean = False
        Dim bld As New System.Text.StringBuilder

        Dim line As String = reader.ReadLine()
        Do While Not line Is Nothing
            bld.Append(line)
            'if at next command prompt then kick out
            If promptPattern.IsMatch(line) Then Exit Do

            'look for free space section header
            If freeSpacePattern.IsMatch(line) Then
                isFreeSpaceSection = True
                line = reader.ReadLine()
                Continue Do
            End If

            'allow one empty line
            If String.IsNullOrWhiteSpace(line) Then
                emptyLineCount += 1
                If emptyLineCount > 2 Then Exit Do
                line = reader.ReadLine()
                Continue Do
            End If

            Dim rowMatch As Match = rowPattern.Match(line)
            If Not rowMatch.Success Then
                noMatchCount += 1
                If noMatchCount > 2 Then Exit Do
                line = reader.ReadLine()
                Continue Do
            End If

            If isFreeSpaceSection Then
                'update existing heap with free space and pct info
                Dim heap = (From i In info.Heaps Where i.Id = CInt(rowMatch.Groups.Item(1).Value) Select i).FirstOrDefault()
                If Not heap Is Nothing Then
                    heap.Gen0.FreeSpace = CLng(rowMatch.Groups.Item(2).Value)
                    heap.Gen1.FreeSpace = CLng(rowMatch.Groups.Item(3).Value)
                    heap.Gen2.FreeSpace = CLng(rowMatch.Groups.Item(4).Value)
                    heap.LOHeap.FreeSpace = CLng(rowMatch.Groups.Item(5).Value)
                End If

                Dim pctMatch As Match = percentagePattern.Match(line)
                If pctMatch.Success Then
                    heap.SOHPercent = CDbl(pctMatch.Groups.Item(1).Value)
                    heap.LOHPercent = CDbl(pctMatch.Groups.Item(2).Value)
                End If
            Else
                'add new heap with size info
                Dim heap As New HeapInfo With {.Id = CInt(rowMatch.Groups.Item(1).Value),
                                               .Gen0 = New GenerationInfo() With {.Size = CLng(rowMatch.Groups.Item(2).Value)},
                                               .Gen1 = New GenerationInfo() With {.Size = CLng(rowMatch.Groups.Item(3).Value)},
                                               .Gen2 = New GenerationInfo() With {.Size = CLng(rowMatch.Groups.Item(4).Value)},
                                               .LOHeap = New GenerationInfo() With {.Size = CLng(rowMatch.Groups.Item(5).Value)}
                                             }
                info.Heaps.Add(heap)
            End If

            line = reader.ReadLine()
        Loop

        Dim eeMatch As Match = eeheapPattern.Match(text)
        If eeMatch.Success Then
            info.DbgOutput = info.DbgOutput & String.Format("{0}{1}", Environment.NewLine, eeMatch.Value)
        End If
        info.DbgOutput = bld.ToString()
        Return info

    End Function

End Class

